import Blockutils from "@/utils/BlockUtils";
import Clipboard from "@/methods/Clipboard";
import Mota, { MotaInterface } from "@/mota";
import BuildMap from "@/mota/utils/BuildUtils/BuildMap";
import BuildFloor from "@/mota/utils/BuildUtils/BuildFloor";
import BuildLayer from "@/mota/utils/BuildUtils/BuildLayer";
import BuildSelect from "@/mota/utils/BuildUtils/BuildSelect";
import BuildAppend from "@/mota/utils/BuildUtils/BuildAppend";
import { GetMap, GetMapAll, MapInfo, UnitInfo } from "@/mota/data/MapDataUtils";

export type BuildState = "none" | "select" | "append";

export default class BuildUtils implements MotaInterface {

	public readonly Mota: Mota;

	public buildState: BuildState = "none";

	public range: "map" | "draw" | undefined = undefined;

	public mapList: MapInfo[] = [];

	public map: MapInfo = { floor: -1, name: "error", unit: new Set };

	public append: BuildAppend;

	public select: BuildSelect;

	public layer: BuildLayer;

	public mapUnit: BuildMap;

	// public buildFloor: BuildFloor;

	public ZIndex: number = 9;

	public Children: MotaInterface["Children"];

	public block: Blockutils = new Blockutils();

	public constructor(mota: Mota) {
		this.Mota = mota;
		this.append = new BuildAppend(this);
		this.select = new BuildSelect(this);
		this.layer = new BuildLayer(this);
		this.mapUnit = new BuildMap(this);
		// this.buildFloor = new BuildFloor(this);
		this.Children = [
			this.select,
			this.append,
			this.layer,
			this.mapUnit,
			// this.buildFloor,
		];
	}

	public get isOpen() {
		return this.block.IsBlockUp;
	}

	public Open(): Promise<void> {
		const { Mota: { map } } = this;
		return this.block.Wait(async () => {
			this.LoadMap();
			this.layer.CalcSize();
			// this.buildFloor.CalcSize();
			this.select.unit = undefined;
		}, () => {
			map.ResetMapMap(this.map);
		});
	}

	public LoadMap(): void {
		const { Mota: { tip, map } } = this;
		const mapAll = GetMapAll();
		const info = GetMap(map.floor);
		if (info === undefined) {
			tip.AddMessage(`目的地图为未知区域`);
		} else {
			this.map = info;
		}
		this.mapList = mapAll;
	}

	public JudgeRange(tx: number, ty: number) {
		if (tx >= 0 && ty >= 0 && tx < 15 && ty < 11) {
			if (tx >= 0 && tx < 4) {
				return "map"
			} else {
				return "draw"
			}
		}
		return undefined;
	}

	public JudgeRangeDraw(tx: number, ty: number): boolean {
		return tx >= 0 && ty >= 0 && tx < 11 && ty < 11;
	}

	public SetRange(tx: number, ty: number) {
		this.range = this.JudgeRange(tx, ty);
	}

	public GetUnit(tx: number, ty: number): UnitInfo[] | undefined {
		const { Mota: { map } } = this;

		return Array.from(this.map.unit).reverse().filter(unit => {
			const size = unit[2];
			return map.EqualsSize(tx, ty, unit[2]);
		});
	}

	public AppendUnit(info: UnitInfo) {
		const { layer, map: { unit } } = this;

		unit.add(info);
		layer.AppendUnit(info);
	}

	public DeleteUnit(info: UnitInfo) {
		const { layer, select, map: { unit } } = this;

		unit.delete(info);
		layer.DeleteUnit(info);
		select.DeleteUnit(info);
		this.buildState = "none";
	}

	public CopyMapData(): void {
		const { map, Mota: { tip } } = this;
		const { floor, name, unit } = map;
		const configStr = ` {
        floor: ${JSON.stringify(floor)},
        name: ${JSON.stringify(name)},
        unit: new Set<UnitInfo>([
            ${Array.from(unit).map(
			([name, show, size, state, event]) => `[${JSON.stringify(name)
				}, ${JSON.stringify(show)
				}, [${size.join(", ")
				}], ${JSON.stringify(state)
				}${event === undefined ? "" : `, new Set<EventType>(${JSON.stringify(Array.from(event))})`
				}]`
		).join(",\n            ")}
        ]),
    },`;
		Clipboard(configStr);
		tip.AddMessage("复制地图数据成功");
	}

	public InView(): boolean {
		const { isOpen } = this;
		return isOpen;
	}

	public JudgeKey: MotaInterface["JudgeKey"] = (keyUtils): void => {
		if (!this.InView()) { return; }

		const { Mota, buildState } = this;

		for (const key of keyUtils.keyHistory) {
			if (key === "b" && keyUtils.has("Control") && buildState === "none") {
				this.block.Release();
				keyUtils.delete(key);
			}
			else if (key === "m" && keyUtils.has("Control")) {
				this.CopyMapData();
				keyUtils.delete(key);
			}
		}
	}

	public click: MotaInterface["click"] = ([tx, ty]) => {
		if (!this.InView()) { return; }
		this.SetRange(tx, ty);
	}

	public mousedown: MotaInterface["mousedown"] = ([tx, ty]) => {
		if (!this.InView()) { return; }
		this.SetRange(tx, ty);
	}

	public mousemove: MotaInterface["mousemove"] = ([tx, ty]) => {
		if (!this.InView()) { return; }
		this.SetRange(tx, ty);
	}

	public mouseup: MotaInterface["mouseup"] = ([tx, ty]) => {
		if (!this.InView()) { return; }
		this.SetRange(tx, ty);
	}

	public Draw: MotaInterface["Draw"] = (draw) => {
		if (!this.InView()) { return; }

		const { _2d } = draw;
		const { Mota: { build: { buildState, select, append, layer } } } = this;

		_2d.save();

		draw.translate(4, 0);

		{
			const { isOpen, unit } = select;
			if (isOpen && unit !== undefined) {
				const [, , size] = unit;
				if (size.length === 2) {
					const [x, y] = size;
					_2d.strokeStyle = "yellow";
					draw.SetLineWidth(1 / 16);
					draw.strokeRect(x, y, 1, 1);
				} else {
					const [x, y, x2, y2] = size;
					const w = x2 - x + 1;
					const h = y2 - y + 1;
					_2d.strokeStyle = "yellow";
					draw.SetLineWidth(1 / 16);
					draw.strokeRect(x, y, w, h);
				}
			}
		}

		{
			const { state, image, pos } = append;
			if ((state === "appendSelect" || state === "appendStart") && image !== undefined) {
				if (pos !== undefined) {
					_2d.save();

					_2d.globalAlpha = 0.5;
					draw.DrawBackground([image, pos]);

					_2d.restore();
				}
			}
		}

		{
			const { SelectItem } = layer;
			if (buildState === "none" && SelectItem !== undefined) {
				const [, , size] = SelectItem;
				if (size.length === 2) {
					const [x, y] = size;
					_2d.strokeStyle = "#00FF00";
					draw.SetLineWidth(1 / 16);
					draw.strokeRect(x, y, 1, 1);
				} else {
					const [x, y, x2, y2] = size;
					const w = x2 - x + 1;
					const h = y2 - y + 1;
					_2d.strokeStyle = "#00FF00";
					draw.SetLineWidth(1 / 16);
					draw.strokeRect(x, y, w, h);
				}
			}
		}

		_2d.restore();
	}
}
